package com.ledou44.ledourpc.aop;

import com.alibaba.fastjson.JSON;
import com.ledou44.ledourpc.client.Client;
import com.ledou44.ledourpc.dto.LedouMethod;
import com.ledou44.ledourpc.dto.LedouRpcDTO;
import com.ledou44.ledourpc.dto.LedouSradDTO;
import com.ledou44.ledourpc.dto.ServiceDTO;
import com.ledou44.ledourpc.sradenum.DiscoveryResultEnum;
import com.ledou44.ledourpc.sradenum.LedouSradEnum;
import lombok.extern.slf4j.Slf4j;
import org.springframework.cglib.proxy.MethodInterceptor;
import org.springframework.cglib.proxy.MethodProxy;

import java.lang.reflect.Method;

/**
 * MethodInterceptor是CGLib的动态代理，对接口/类都能进行增强
 */
@Slf4j
public class ReferenceAop implements MethodInterceptor {

    /**
     * netty客户端
     */
    private final Client client;

    /**
     * 被代理的类
     */
    private final Class<?> cls;

    public ReferenceAop(Client client, Class<?> cls) {
        this.client = client;
        this.cls = cls;
    }

    /**
     * 环绕增强方法，在该环绕增强方法中也可以用method.invoke()调用原方法
     * 在前面Enhancer.create()生成代理类时，会根据该方法的规则环绕增强原对象的每个方法，
     * 之后每一次调用接口的方法时其实都会走intercept方法，这个方法就替代了原方法
     * @param o
     * @param method 代理方法
     * @param objects 方法入参
     * @param methodProxy
     * @return
     * @throws Throwable
     */
    @Override
    public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
        LedouSradDTO ledouSradDTO = new LedouSradDTO();
        ledouSradDTO.setType(LedouSradEnum.SERVICE_DISCOVERY.getCode());
        ledouSradDTO.setServiceName(cls.getName());
        //与服务注册与发现中心通讯获取调用服务的地址,这里会调用doRun()方法开启一次netty-tcp通信
        client.serviceDiscovery(ledouSradDTO);
        if(ledouSradDTO.getDiscoveryResult().equals(DiscoveryResultEnum.SUCCESS.getCode())) {
            ServiceDTO service = ledouSradDTO.getService();
            LedouRpcDTO message = new LedouRpcDTO();
            LedouMethod postMethod = new LedouMethod();
            postMethod.setName(method.getName());
            postMethod.setParameterTypes(method.getParameterTypes());
            postMethod.setReturnType(method.getReturnType());
            message.setMethod(postMethod);
            message.setObjects(objects);
            message.setBaseClass(cls);
            //根据ip和端口调用服务提供者的微服务，拿到方法执行返回结果，这里会调用doRun()方法开启一次netty-tcp通信
            //所以代码中每一次调用增强的方法时，都会触发intercept方法，并通过RPC交互调用服务提供者拿到返回值。
            client.run(message, service.getAddress(), service.getPort());
            if(message.getSuccess()) {
                return JSON.parseObject(String.valueOf(message.getResult()), method.getReturnType());
            }
        }
        return null;
    }
}
